package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.album.service.AuditService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.atguigu.tingshu.common.constant.SystemConstant.*;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;
    @Autowired
    private AlbumStatMapper albumStatMapper;
    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private AuditService auditService;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    //保存专辑信息
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo, Long userId) {
        //1.保存专辑信息
        //1.1 将专辑VO转为PO对象-》持久化对象
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        //1.2 为专辑属性赋值：用户ID，试听集数，审核状态
        albumInfo.setUserId(userId);
        String payType = albumInfo.getPayType();
        if (ALBUM_PAY_TYPE_VIPFREE.equals(payType) || ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
            //vip免费，付费资源设置试听集
            albumInfo.setTracksForFree(3);//免费听三集
        }
        //未通过审核
        albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
        //1.3 保存专辑，得到专辑ID
        albumInfoMapper.insert(albumInfo);
        Long albumId = albumInfo.getId();
        //2.保存标签信息
        //2.1 获取VO中提交专辑标签关系集合
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (CollUtil.isNotEmpty(albumAttributeValueVoList)) {
            //2.2 为专辑标签关联专辑ID，"批量"新增专辑标签关系
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue =
                        BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
                albumAttributeValue.setAlbumId(albumId);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        //3.初始化专辑统计信息
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_PLAY, 0);
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_SUBSCRIBE, 0);
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_BUY, 0);
        this.saveAlbumInfoStat(albumId, ALBUM_STAT_COMMENT, 0);


        //4. 对专辑中文本内容进行审核  & 索引库ES中新增记录
        String text = albumInfo.getAlbumTitle() + albumInfo.getAlbumIntro();
        String suggestion = auditService.auditText(text);
        if ("pass".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
            //4.采用RabbitMQ可靠性消息将过审专辑存入ES索引库
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumId);
        } else if ("block".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_NO_PASS);
        } else if ("review".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_ARTIFICIAL);
        }
        albumInfoMapper.updateById(albumInfo);
    }


    /**
     * 保存专辑统计信息
     *
     * @param albumId  专辑ID
     * @param statType 统计类型
     * @param statNum  统计数值 0401-播放量 0402-订阅量 0403-购买量 0403-评论数'
     */
    //保存专辑统计信息
    @Override
    public void saveAlbumInfoStat(Long albumId, String statType, int statNum) {
        AlbumStat albumStat = new AlbumStat();
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statType);
        albumStat.setStatNum(statNum);
        albumStatMapper.insert(albumStat);
    }


    /**
     * 分页条件查询当前登录用户发布专辑
     *
     * @param pageParam      MP分页对象
     * @param albumInfoQuery 查询条件
     * @return
     */
    //分页条件查询当前登录用户发布专辑
    @Override
    public Page<AlbumListVo> findUserAlbumPage(Page<AlbumListVo> pageParam, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.findUserAlbumPage(pageParam, albumInfoQuery);
    }


    //删除专辑
    @Override
    public void deltteAlbum(Long id) {
        //1.根据专辑ID查询声音表判断该专辑下是否关联声音 如果存在 不允许删除
        Long count = trackInfoMapper.selectCount(
                new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, id)
        );
        if (count > 0) {//声音属于核心资产，不能删除
            throw new GuiguException(500, "该专辑下存在关联声音");
        }
        //2.删除专辑
        //没声音。可以删除
        albumInfoMapper.deleteById(id);
        //3.删除专辑标签关系
        albumAttributeValueMapper.delete(
                new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id)
        );
        //4.删除专辑统计信息
        albumStatMapper.delete(
                new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, id)
        );
        //5. 基于MQ删除存在Elasticsearch（全文搜索引擎）中数据
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);


    }

    @Override
    public AlbumInfo getAlbumInfo(Long id) {
        try {
            //1.优先从缓存Redis中获取业务数据
            //1.1 构建专辑业务数据Key 形式：前缀+专辑ID
            String dataKey = RedisConstant.ALBUM_INFO_PREFIX + id;
            //1.2从 Redis 缓存中获取 AlbumInfo 对象
            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
            if (albumInfo != null) {
                //2.缓存有数据，直接返回
                return albumInfo;
            }
            //3.缓存无数据，先获取分布式锁
            //key
            String lockKey = dataKey + RedisConstant.CACHE_INFO_PREFIX;
            //创建锁对象
            RLock lock = redissonClient.getLock(lockKey);
            //获取锁
            boolean flag = lock.tryLock();
            if (flag) {//4.获取分布锁锁成功，执行查询数据库，并设置缓存
                try {
                    AlbumInfo albumInfoFromDB = this.getAlbumInfoFromDB(id);
                    //等待时间
                    int ttl = RandomUtil.randomInt(100, 600);
                    redisTemplate.opsForValue().set(dataKey, albumInfoFromDB,
                            RedisConstant.ALBUM_TIMEOUT + ttl, TimeUnit.SECONDS);
                    return albumInfoFromDB;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }finally {
                    //5释放锁
                    lock.unlock();
                }
            } else {//6.获取分布式锁失败，，自旋
                //睡眠+自旋
                TimeUnit.SECONDS.sleep(30);
                return this.getAlbumInfo(id);
            }
        } catch (Exception e) {
            log.error("[专辑服务]获取专辑信息锁服务异常异常：{},执行兜底处理方案：{}", e, "直接查询数据库");
            return this.getAlbumInfoFromDB(id);
        }
    }


    //修改专辑---之---回显数据---从数据库中获取
    @Override
    public AlbumInfo getAlbumInfoFromDB(Long id) {
        //查用户的信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        //查属性信息
        if (albumInfo != null) {
            //非空可改
            List<AlbumAttributeValue> albumAttributeValues =
                    albumAttributeValueMapper.selectList(
                            new LambdaQueryWrapper<AlbumAttributeValue>()
                                    .eq(AlbumAttributeValue::getAlbumId, id)
                    );
            //传入属性集合
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        }
        return albumInfo;
    }


    //更新专辑信息
    @Override
    //当方法执行过程中抛出任何 Exception 类型的异常时，事务会自动回滚
    @Transactional(rollbackFor = Exception.class)
    public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {
        /*
         * 更新的条件就是先下架，在更新
         * */
        //1.更新专辑信息 状态：未审核
        AlbumInfo albumInfo = BeanUtil.copyProperties(albumInfoVo, AlbumInfo.class);
        albumInfo.setId(id);
        albumInfo.setStatus(ALBUM_STATUS_NO_PASS);
        albumInfoMapper.updateById(albumInfo);
        //2.更新专辑标签关系，先删除一起阿曼德标签，再新增
        //2.1 根据专辑ID删除原有标签关系
        //不用deletebyid是因为一的id对应的attribute多条，用它只能删除一条记录
        albumAttributeValueMapper.delete(
                //是mybatis的条件查询构造器，避免售资额sql，防止sql注入
                new LambdaQueryWrapper<AlbumAttributeValue>()
                        //where getAlbumId = id
                        .eq(AlbumAttributeValue::getAlbumId, id)
        );
        //2.2 从VO中获取提交专辑标签关系集合 再次批量保
        List<AlbumAttributeValueVo> albumAttributeValueVoList =
                albumInfoVo.getAlbumAttributeValueVoList();
        if (CollUtil.isNotEmpty(albumAttributeValueVoList)) {
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                //将VO对象转换为实体类对象
                //将前端传递的专辑标签数据保存到数据库-》VO 转实体
                AlbumAttributeValue albumAttributeValue =
                        BeanUtil.copyProperties(albumAttributeValueVo, AlbumAttributeValue.class);
                //BeanUtils版本使用没有返回值的
                albumAttributeValue.setAlbumId(id);
                //插入数据库
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }
        //3. 再次对内容进行审核
        String text = albumInfo.getAlbumTitle() + albumInfo.getAlbumIntro();
        String suggestion = auditService.auditText(text);
        if ("pass".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
            //3.采用RabbitMQ可靠性消息将过审专辑存入ES索引库
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, id);
        } else if ("block".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_NO_PASS);
        } else if ("review".equals(suggestion)) {
            albumInfo.setStatus(SystemConstant.ALBUM_STATUS_ARTIFICIAL);
        }
        albumInfoMapper.updateById(albumInfo);

    }


    //获取当前用户全部专辑列表
    @Override
    public List<AlbumInfo> getUserAllAlbumList(Long userId) {
        //1.构建条件查询条件QueryWrapper对象
        LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(AlbumInfo::getUserId, userId);//查询条件
        queryWrapper.orderByDesc(AlbumInfo::getId);//排序
        queryWrapper.last("LIMIT 200");//限制记录数
        queryWrapper.select(AlbumInfo::getId, AlbumInfo::getAlbumTitle);//指定查询列
        return albumInfoMapper.selectList(queryWrapper);
    }


    /**
     * 根据专辑ID查询专辑统计信息
     *
     * @param albumId
     * @return
     */
    @Override
    public AlbumStatVo getAlbumStatVo(Long albumId) {
        return albumInfoMapper.getAlbumStatVo(albumId);
    }


}







